{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# User's Guide, Chapter 12: Getting Back to Basics: The Music21Object\n",
    "\n",
    "Almost everything that we've been working with so far, `Note` objects, `Chord` objects, `Stream` objects, etc., are subclasses of a object that, for lack of a better name, is called :class:`~music21.base.Music21Object`. \n",
    "\n",
    "A `Music21Object` is something that can go in a `Stream`, knows where it is in a Stream, and has a `Duration` at `.duration`.  \n",
    "\n",
    "Of course, not every object in Python is a `Music21Object`, but a little surprisingly, not every object in `music21` is a \"`Music21Object`\". For instance, the :class:`~music21.pitch.Pitch` object is not. If you try to put one in a `Stream`, you'll get an error:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "tags": [
     "nbval-raises-exception"
    ]
   },
   "outputs": [
    {
     "ename": "StreamException",
     "evalue": "to put a non Music21Object in a stream, create a music21.ElementWrapper for the item",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mStreamException\u001b[0m                           Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-1-29db162cd09d>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0mp\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpitch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mPitch\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"A-2\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      4\u001b[0m \u001b[0ms\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mstream\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mStream\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 5\u001b[0;31m \u001b[0ms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minsert\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mp\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m~/git/music21base/music21/stream/__init__.py\u001b[0m in \u001b[0;36minsert\u001b[0;34m(self, offsetOrItemOrList, itemOrNone, ignoreSort, setActiveSite)\u001b[0m\n\u001b[1;32m   1754\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1755\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mitem\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbase\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mMusic21Object\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1756\u001b[0;31m             raise StreamException('to put a non Music21Object in a stream, '\n\u001b[0m\u001b[1;32m   1757\u001b[0m                                   + 'create a music21.ElementWrapper for the item')\n\u001b[1;32m   1758\u001b[0m         \u001b[0melement\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mStreamException\u001b[0m: to put a non Music21Object in a stream, create a music21.ElementWrapper for the item"
     ]
    }
   ],
   "source": [
    "from music21 import *\n",
    "\n",
    "p = pitch.Pitch(\"A-2\")\n",
    "s = stream.Stream()\n",
    "s.insert(0, p)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    ":class:`~music21.duration.Duration`s are also not `Music21Objects`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "tags": [
     "nbval-raises-exception"
    ]
   },
   "outputs": [
    {
     "ename": "StreamException",
     "evalue": "to put a non Music21Object in a stream, create a music21.ElementWrapper for the item",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mStreamException\u001b[0m                           Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-2-a674eb658c1c>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0md\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mduration\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mDuration\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'half'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0ms\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0minsert\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0md\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m~/git/music21base/music21/stream/__init__.py\u001b[0m in \u001b[0;36minsert\u001b[0;34m(self, offsetOrItemOrList, itemOrNone, ignoreSort, setActiveSite)\u001b[0m\n\u001b[1;32m   1754\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1755\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mitem\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbase\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mMusic21Object\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1756\u001b[0;31m             raise StreamException('to put a non Music21Object in a stream, '\n\u001b[0m\u001b[1;32m   1757\u001b[0m                                   + 'create a music21.ElementWrapper for the item')\n\u001b[1;32m   1758\u001b[0m         \u001b[0melement\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mitem\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mStreamException\u001b[0m: to put a non Music21Object in a stream, create a music21.ElementWrapper for the item"
     ]
    }
   ],
   "source": [
    "d = duration.Duration('half')\n",
    "s.insert(0, d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Why don't we just make everything a `Music21Object`? There's an overhead in making a Music21Object, so if we did that, the system would probably run about 10x slower than it does. But there's no reason to put a `Pitch` or a `Duration` in a Stream, when a `Note` is basically a `Pitch` plus a `Duration`.  This works much better:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0.0} <music21.note.Note A->\n"
     ]
    }
   ],
   "source": [
    "n = note.Note('A-2', type='half')\n",
    "s.insert(0, n)\n",
    "s.show('text')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "How can we tell that a `Note` is a `Music21Object`? Well we can read the docs (:class:`~music21.note.Note`) where it says:\n",
    "\n",
    "**Note** `bases`:\n",
    "    \n",
    "    * NotRest\n",
    "    * GeneralNote\n",
    "    * Music21Object\n",
    "    \n",
    "Or we can use the `isinstance(obj, class)` operator on a given note. The class we are looking for is `base.Music21Object`. We still have our A♭ as `n`, so we can do:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(n, base.Music21Object)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that we need to have a Note object first, we can't test the class itself:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "isinstance(note.Note, base.Music21Object)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There's one other way that you can tell if an object is a `Music21Object`, that's to check whether `Music21Object` appears in the object's `.classes`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'Music21Object' in n.classes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But that's a bit of a cop-out.  Things that are not `Music21Objects` don't generally have a `.classes` property, so that won't work:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "datetime.datetime(2015, 9, 27, 0, 0)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import datetime\n",
    "dt = datetime.datetime(2015, 9, 27)\n",
    "dt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "tags": [
     "nbval-raises-exception"
    ]
   },
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'datetime.datetime' object has no attribute 'classes'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-8-f578ffe0ec17>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;34m'Music21Object'\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mdt\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclasses\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m: 'datetime.datetime' object has no attribute 'classes'"
     ]
    }
   ],
   "source": [
    "'Music21Object' in dt.classes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But it's a useful and easy way of checking to see if something you know *is* a `Music21Object` is a specific class:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'Chord' in n.classes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In fact, it's useful enough that we've put it in a few objects in `music21` that aren't `Music21Objects`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'Duration' in d.classes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The class of an object cannot (well, should not) change after it's been created.  Thus it can be thought of as totally stable. Streams have many ways of filtering out `Music21Object`s (a.k.a. \"elements\") according to class.  The easiest way is with `.getElementsByClass`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<music21.note.Note A>\n",
      "<music21.note.Note B>\n"
     ]
    }
   ],
   "source": [
    "s = stream.Stream()\n",
    "s.append(clef.TrebleClef())\n",
    "s.append(meter.TimeSignature('3/4'))\n",
    "s.append(note.Note(\"A\"))\n",
    "s.append(note.Rest())\n",
    "s.append(note.Note(\"B\"))\n",
    "\n",
    "for element in s.getElementsByClass('Note'):\n",
    "    print(element)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you wanted to get the Notes and the Rest, you could figure out what their common ancestor class is and filter on that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('Rest', 'GeneralNote', 'Music21Object', 'ProtoM21Object', 'object')"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "note.Rest().classes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "('Note', 'NotRest', 'GeneralNote', 'Music21Object', 'ProtoM21Object', 'object')"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "note.Note().classes"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "aha! The common ancestor class is `GeneralNote` so we will filter on this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<music21.note.Note A>\n",
      "<music21.note.Rest rest>\n",
      "<music21.note.Note B>\n"
     ]
    }
   ],
   "source": [
    "for element in s.getElementsByClass('GeneralNote'):\n",
    "    print(element)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Or you could give a list of relevant classes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<music21.note.Note A>\n",
      "<music21.note.Rest rest>\n",
      "<music21.note.Note B>\n"
     ]
    }
   ],
   "source": [
    "for element in s.getElementsByClass(['Note', 'Rest']):\n",
    "    print(element)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For those who are familiar with HTML/Javascript/DOM programming, a Python Class is most similar to a DOM Tag (like &lt;img&gt;). So DOM methods such as `.getElementsByTagName` are similar to `music21`'s `.getElementsByClass`.\n",
    "\n",
    "It's worth pointing out that there's also a `.classSet` (v.2.1+) on every `Music21Object` that gives the string name of all base classes, the fully-qualified string name for every class, as well as the class object.  Since it will return `True` for basically every form that someone might pass in a class selector, it's used a lot internally for safety. It returns these objects as a `frozenset()`, meaning it will return in any order and cannot be manipulated:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "tags": [
     "nbval-ignore-output"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "frozenset({music21.base.Music21Object,\n",
       "           music21.note.GeneralNote,\n",
       "           music21.note.Rest,\n",
       "           music21.prebase.ProtoM21Object,\n",
       "           object,\n",
       "           'GeneralNote',\n",
       "           'Music21Object',\n",
       "           'ProtoM21Object',\n",
       "           'Rest',\n",
       "           'builtins.object',\n",
       "           'music21.base.Music21Object',\n",
       "           'music21.note.GeneralNote',\n",
       "           'music21.note.Rest',\n",
       "           'music21.prebase.ProtoM21Object',\n",
       "           'object'})"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r = note.Rest()\n",
    "r.classSet"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(True, True, True)"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "('Rest' in r.classSet, 'music21.note.GeneralNote' in r.classSet, base.Music21Object in r.classSet)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Attributes and Properties"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "Once you know that something is a `music21Object` you can exploit the attributes of the object for musical purposes.\n",
    "\n",
    "### `id`\n",
    "\n",
    "Each `music21Object` has a (should be) unique id stored in the `.id` attribute:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "tags": [
     "nbval-ignore-output"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4540923128"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n = note.Note(\"C#4\")\n",
    "n.id"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By default, this `.id` is the same as the location of the object in memory, which the built-in Python function `id()` returns:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "tags": [
     "nbval-ignore-output"
    ]
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4540923128"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "id(n)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But we can set it manually so that the object is easier to find later:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'first_note'"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n.id = 'first_note'\n",
    "n.id"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We advise `.id` not to include spaces or special characters, and may enforce it in the future.\n",
    "\n",
    "This `.id` is especially useful for `Stream` objects because it will be displayed in the representation of the Stream and, if there's no other metadata, can be used as the name of the part:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<music21.stream.Stream empty_stream>"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "s = stream.Stream()\n",
    "s.id = 'empty_stream'\n",
    "s"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Parts can be retrieved from the `.parts` attribute of a score by id."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<music21.stream.Part Soprano>"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "bach = corpus.parse('bwv66.6')\n",
    "sopr = bach.parts['soprano']\n",
    "sopr"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Soprano'"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sopr.id"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There are some properties such as `.getElementById()` that retrieve objects by `.id`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<music21.note.Note C#> first_note\n"
     ]
    }
   ],
   "source": [
    "s.append(n)\n",
    "x = s.getElementById('first_note')\n",
    "print(x, x.id)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you know HTML/Javascript/DOM programming, the similarity between `music21`'s `.getElementById()` and HTML's `.getElementById()` is intentional."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Groups\n",
    "\n",
    "A group is a collection of labels for an object.  Think of :class:`~music21.base.Groups` as being like `.id` with two differences: (1) each `Music21Object` can have zero, one, or multiple Groups -- but it has exactly one `.id` and (2) a single group label can belong to multiple `Music21Objects`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[]"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n.groups"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Groups` are wrappers around lists that enforce the restriction that the label must be a string.  Since they're otherwise just lists, you can add a group to any object just by appending a string to the group:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['black_key', 'sharped']"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n.groups.append('black_key')\n",
    "n.groups.append('sharped')\n",
    "n.groups"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We advise groups not to have spaces in them and will be enforcing this in v.3.\n",
    "\n",
    "Now we can search through Streams that `n` is in to find it by searching for the group `'sharped'` (or `'black key'`). We use `Stream.getElementsByGroup()`.  Note that it is plural `Elements` while the previous call was `getElementById` singular. That's because there should be only one object with each id but there could be many with the same group:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<music21.note.Note C#> first_note\n"
     ]
    }
   ],
   "source": [
    "for x in s.getElementsByGroup('sharped'):\n",
    "    print(x, x.id)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Groups` are the equivalent of the HTML/Javascript/DOM \"class\".  (Since `class` means something else in Python, we've changed the term).  Eventually, `Groups` will be able to be used in styling objects automatically.  For now we can just do it by hand:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "metadata": {
      "image/png": {
       "height": 56,
       "width": 170
      }
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "for x in s.iter.getElementsByGroup('black_key'):\n",
    "    x.notehead = 'circle-x'\n",
    "    \n",
    "s.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### ActiveSite"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A `Music21Object` that is inside one or more Streams should be able to get its most recently stream via its `.activeSite` attribute.  We've put `n` in `s`, which is called (now incorrectly) `'empty stream'`, so n's `.activeSite` should be `s`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<music21.stream.Stream empty_stream>"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n.activeSite"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The activeSite may change over time; obviously if the note is put in another Stream then that Stream will become the activeSite.  Let's put the note in a new stream, four quarter notes from the start:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<music21.stream.Stream new_stream>"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "t = stream.Stream()\n",
    "t.id = 'new_stream'\n",
    "t.insert(4.0, n)\n",
    "\n",
    "n.activeSite"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can also change the activeSite..."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<music21.stream.Stream empty_stream>"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n.activeSite = s\n",
    "n.activeSite"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As long as it is a Stream that the Element is already a part of:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "tags": [
     "nbval-raises-exception"
    ]
   },
   "outputs": [
    {
     "ename": "SitesException",
     "evalue": "activeSite cannot be set for object <music21.note.Note C#> not in the Stream <music21.stream.Stream unrelated_stream>",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mKeyError\u001b[0m                                  Traceback (most recent call last)",
      "\u001b[0;32m~/git/music21base/music21/stream/__init__.py\u001b[0m in \u001b[0;36melementOffset\u001b[0;34m(self, element, stringReturns)\u001b[0m\n\u001b[1;32m   1639\u001b[0m             \u001b[0;31m# 2.3 million times found in TestStream\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1640\u001b[0;31m             \u001b[0mo\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_offsetDict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0melement\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1641\u001b[0m             \u001b[0;31m# if returnedElement is not element:  # stale reference...\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mKeyError\u001b[0m: 4540923128",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[0;31mSitesException\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m~/git/music21base/music21/base.py\u001b[0m in \u001b[0;36m_setActiveSite\u001b[0;34m(self, site)\u001b[0m\n\u001b[1;32m   2037\u001b[0m             \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2038\u001b[0;31m                 \u001b[0mstoredOffset\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0msite\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0melementOffset\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   2039\u001b[0m             \u001b[0;32mexcept\u001b[0m \u001b[0mSitesException\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;32m~/git/music21base/music21/stream/__init__.py\u001b[0m in \u001b[0;36melementOffset\u001b[0;34m(self, element, stringReturns)\u001b[0m\n\u001b[1;32m   1650\u001b[0m                     \u001b[0;34m'an entry for this object 0x%x is not stored in stream %s'\u001b[0m \u001b[0;34m%\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1651\u001b[0;31m                     (id(element), self))\n\u001b[0m\u001b[1;32m   1652\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mSitesException\u001b[0m: an entry for this object 0x10ea8fcf8 is not stored in stream <music21.stream.Stream unrelated_stream>",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[0;31mSitesException\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-33-aa8ad4f44014>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mq\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mstream\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mStream\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mid\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'unrelated_stream'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mn\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mactiveSite\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mq\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m~/git/music21base/music21/base.py\u001b[0m in \u001b[0;36m_setActiveSite\u001b[0;34m(self, site)\u001b[0m\n\u001b[1;32m   2039\u001b[0m             \u001b[0;32mexcept\u001b[0m \u001b[0mSitesException\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   2040\u001b[0m                 raise SitesException('activeSite cannot be set for '\n\u001b[0;32m-> 2041\u001b[0;31m                                      + f'object {self} not in the Stream {site}')\n\u001b[0m\u001b[1;32m   2042\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   2043\u001b[0m             \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_activeSiteStoredOffset\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mstoredOffset\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
      "\u001b[0;31mSitesException\u001b[0m: activeSite cannot be set for object <music21.note.Note C#> not in the Stream <music21.stream.Stream unrelated_stream>"
     ]
    }
   ],
   "source": [
    "q = stream.Stream(id='unrelated_stream')\n",
    "n.activeSite = q"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Newly created objects have an `.activeSite` of `None`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "m = note.Rest()\n",
    "m.activeSite is None"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `.activeSite` of an object will determine which other objects it is connected to, where it thinks it is, etc. The best way to demonstrate that is with the next attribute..."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### offset\n",
    "\n",
    "The `.offset` of a `Music21Object` is the number of quarter notes from the start of the Stream it is a part of. The Stream that is referenced is the `.activeSite`. Remember that `n` was inserted at offset 0 of `s` (`'empty stream'`) and offset 4 of `t` (`'new stream'`):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.0"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n.activeSite = s\n",
    "n.offset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4.0"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n.activeSite = t\n",
    "n.offset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we change the offset of the `Note` it changes it in the `Stream`, so that if we change the activeSite away and back, the offset is preserved. It's more easily demonstrated than explained in words:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.0"
      ]
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n.activeSite = s\n",
    "n.offset = 2.0\n",
    "n.activeSite = t\n",
    "n.activeSite = s\n",
    "n.offset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "A newly created `Music21Object` has a great advantage -- it can set its offset to anything it wants and then when it is inserted into a Stream with a single argument, it appears at that offset."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<music21.stream.Stream empty_stream>"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n2 = note.Note('G-2')\n",
    "n2.offset = 20.0\n",
    "s.insert(n2)\n",
    "n2.activeSite"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "20.0"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "n2.offset"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### priority\n",
    "\n",
    "If you have a Stream with two elements at the same offset, how can you know which one of them should come first? The easiest way to ensure that one comes before the other is to change the `.priority` of one of them.  `.priority` is any integer, with a default of zero.  Let's create some a new Stream and some notes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0.0} <music21.note.Note D>\n",
      "{0.0} <music21.note.Note E>\n"
     ]
    }
   ],
   "source": [
    "s = stream.Stream()\n",
    "d = note.Note('D4')\n",
    "e = note.Note('E4')\n",
    "s.insert(0.0, d)\n",
    "s.insert(0.0, e)\n",
    "s.show('text')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Both notes are at offset 0, but D was inserted first, so it comes first.  But we can move E by making it's `.priority` lower than D's:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "d.priority"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0.0} <music21.note.Note E>\n",
      "{0.0} <music21.note.Note D>\n"
     ]
    }
   ],
   "source": [
    "e.priority = -1\n",
    "s.show('text')"
   ]
  },
  {
   "cell_type": "raw",
   "metadata": {},
   "source": [
    ".. note::\n",
    "\n",
    "   Think of `priority` like a number line.  So negative numbers come before positive.  Don't think of a\n",
    "   \"high priority\" like 2000 meaning that you will encounter that object first. It is the opposite.\n",
    "\n",
    "   Prior to music21 v3, changing the priority of an object did not automatically tell its sites\n",
    "   that they needed to be sorted again.  To get this output in earlier versions, call `s.elementsChanged()`\n",
    "   \n",
    "   If there is enough demand, priority may become a per-site attribute like `offset` but this is a problem we\n",
    "   plan to tackle when need arises."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If we return e's priority to the default of `0`, it will again appear after d:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0.0} <music21.note.Note D>\n",
      "{0.0} <music21.note.Note E>\n"
     ]
    }
   ],
   "source": [
    "e.priority = 0\n",
    "s.show('text')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### classSortOrder\n",
    "\n",
    "Objects seem to be sorted by offset first, then priority, then when they were inserted. But what about this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{0.0} <music21.clef.TrebleClef>\n",
      "{0.0} <music21.note.Note D>\n",
      "{0.0} <music21.note.Note E>\n"
     ]
    }
   ],
   "source": [
    "tc = clef.TrebleClef()\n",
    "s.insert(0.0, tc)\n",
    "s.show('text')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "How did the Stream (correctly) know that the treble clef should come first?  It's not because of its priority:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0, 0, 0)"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(tc.priority, d.priority, e.priority)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It's because there is another property that aids in sorting, and that is called `.classSortOrder`.  We can see that `c`, our treble clef, has a lower `.classSortOrder` than `d` and `e` by virtue of being of the class `Clef`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0, 20, 20)"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(tc.classSortOrder, d.classSortOrder, e.classSortOrder)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`.classSortOrder` is like `.priority` in that lower numbers come first.  We've arbitrarily placed Clef at `0` and Note at `20` and lots of other Classes in between.  `.classSortOrder` is what is called a Class Attribute, meaning that the class objects have this attribute set:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0, 20)"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(clef.TrebleClef.classSortOrder, note.Note.classSortOrder)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And any change in `.classSortOrder` applied to the class changes it for all its members:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(25, 10, 10)"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "clef.TrebleClef.classSortOrder = 25\n",
    "note.Note.classSortOrder = 10\n",
    "(tc.classSortOrder, d.classSortOrder, e.classSortOrder)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "But don't do that! For one thing, the ordering of existing Streams is not changed, for another, we've carefully balanced the `classSortOrder` so that musically logical order is maintained (have you ever seen a score where the first note comes before the first clef?).  So we'll change it back here."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0, 20, 20)"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "clef.TrebleClef.classSortOrder = 0\n",
    "note.Note.classSortOrder = 20\n",
    "(tc.classSortOrder, d.classSortOrder, e.classSortOrder)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "sphinx_links": {
     "any": true
    }
   },
   "source": [
    "If you do need to do something wacky, like have notes before clefs, you can always change the `.priority` instead.\n",
    "\n",
    "For more information about how sorting works, jump to :ref:`Chapter 21: Ordering and Sorting of Stream Elements <usersGuide_21_sorting>`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That seems like a good place for a break. We've got some more to cover, so I've split this chapter into two parts, we'll continue in :ref:`Chapter 13: More Music21Object Attributes and Methods<usersGuide_13_music21object2>`"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
